﻿using System;
using System.Collections.Generic;
using System.Text;

using System.Windows.Forms;
using RollBack.src.ui.ExplorerView;
using RollBackLib.src.utils;
using log4net;
using System.Collections;
using System.Drawing;
using RollBackLib.src.core;

namespace RollBack.src.ui
{
    /// <summary>
    /// Encapsulates a TabPage which contains a split panel.  Left side of the split panel is 
    /// the source file list to be backed up and the right side the 'BackugDestinationPanel' which
    /// contains destination directory, start/stop/pause buttons etc.
    /// </summary>
    public class BackupJobTab : System.Windows.Forms.TabPage
    {
        private System.Windows.Forms.SplitContainer m_splitContainer;
        private static readonly ILog log = LogManager.GetLogger(typeof(BackupJobTab));
        public BackupJobTabEventHandlers m_eventHandlers
        {
            get;
            set;
        }

        /// <summary>
        /// The grid file/directory list view which is at the bottom left
        /// </summary>
        public ExplorerRightPanel m_FileList
        {
            get;
            set;
        }

        /// <summary>
        /// Panel contain the buttons for start/stop/pause etc
        /// </summary>
        public BackupDestinationPanel m_BackupDestPanel
        {
            get;
            set;
        }

        private BackupJob m_backupJob;

        /// <summary>
        /// indexes for image that appears on on the job tab when it is idle/running/paused
        /// </summary>
        public static int m_ImageIdle = 0;
        public static int m_ImageRunning = 1;
        public static int m_ImagePaused = 2;

        public BackupJobTab()
        {
            m_splitContainer = new System.Windows.Forms.SplitContainer();
            // create the tab which accepts 
            m_FileList = new ExplorerRightPanel();
            ExplorerData edata = new RollBack.src.ui.ExplorerView.ExplorerData();
            edata.m_rightData.m_allowDrag = false;
            edata.m_rightData.m_allowDrop = true;
            m_FileList.setExplorerData(edata);
            
            m_BackupDestPanel = new BackupDestinationPanel();

            this.SuspendLayout();
            m_splitContainer.SuspendLayout();
            // 
            // fileList1
            // 
            m_FileList.SuspendLayout();
            m_FileList.AllowDrop = true;
            m_FileList.Dock = System.Windows.Forms.DockStyle.Fill;
            m_FileList.Location = new System.Drawing.Point(0, 0);
            m_FileList.Name = "fileList1";
            m_FileList.TabIndex = 0;
            m_FileList.m_fileListView.KeyUp += new KeyEventHandler(m_fileListView_KeyUp);
            m_FileList.m_fileListView.ItemCheck += new ItemCheckEventHandler(m_fileListView_ItemCheck);
            addContextMenuItems();
            // 
            // backupDestinationPanel1
            // 
            m_BackupDestPanel.SuspendLayout();
            m_BackupDestPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            m_BackupDestPanel.Dock = System.Windows.Forms.DockStyle.Fill;
            m_BackupDestPanel.Location = new System.Drawing.Point(0, 0);
            m_BackupDestPanel.Name = "backupDestinationPanel1";
            m_BackupDestPanel.Size = new System.Drawing.Size(276, 125);
            m_BackupDestPanel.TabIndex = 0;

            this.Location = new System.Drawing.Point(4, 22);
            this.Name = "New Job";
            this.Padding = new System.Windows.Forms.Padding(3);
            this.Size = new System.Drawing.Size(801, 131);
            this.TabIndex = 0;
            this.Text = "BackupSet2";
            this.UseVisualStyleBackColor = true;
            // 
            // splitContainer1
            // 
            m_splitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
            m_splitContainer.Location = new System.Drawing.Point(3, 3);
            m_splitContainer.Name = "splitContainer1";
            // 
            // splitContainer1.Panel1
            // 
            m_splitContainer.Panel1.Controls.Add(m_FileList);
            m_FileList.Dock = System.Windows.Forms.DockStyle.Fill;
            // 
            // splitContainer1.Panel2
            // 
            m_splitContainer.Panel2.Controls.Add(m_BackupDestPanel);
            m_splitContainer.Size = new System.Drawing.Size(795, 125);
            m_splitContainer.SplitterDistance = 400;
            m_splitContainer.TabIndex = 0;

            // 
            // BackupSetComposite
            // 
            this.Controls.Add(m_splitContainer);
            m_FileList.ResumeLayout(false);
            m_BackupDestPanel.ResumeLayout(false);
            this.ResumeLayout(false);
            this.ImageIndex = ImageResources.Indexes.m_playButtonDisabledImage;
            m_splitContainer.Panel1.ResumeLayout(false);
            m_splitContainer.Panel2.ResumeLayout(false);
            m_splitContainer.ResumeLayout(false);
        }

        void m_fileListView_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            m_backupJob.m_Modified = true;
        }

        private void addContextMenuItems()
        {
            MenuItem item1 = new MenuItem("Remove from Backup/Restore", new EventHandler(removeFromBackupRestore));
            MenuItem item2 = new MenuItem("Toggle Enable/Disable", new EventHandler(toggleEnableDisableItem));
            MenuItem item3 = new MenuItem("Enable", new EventHandler(enableItem));
            MenuItem item4 = new MenuItem("Disable", new EventHandler(disableItem));
            m_FileList.addContextMenuItem(item1);
            m_FileList.addContextMenuItem(item2);
            m_FileList.addContextMenuItem(item3);
            m_FileList.addContextMenuItem(item4);
        }

        public void enableItem(object data, EventArgs e)
        {
            m_backupJob.m_Modified = true;
            foreach (ListViewItem item in m_FileList.m_fileListView.SelectedItems)
            {
                enableItem(item);
            }
        }

        private static void enableItem(ListViewItem item)
        {
            item.Checked = true;
            item.ForeColor = Color.Black;
        }

        public void disableItem(object data, EventArgs e)
        {
            m_backupJob.m_Modified = true;
            foreach (ListViewItem item in m_FileList.m_fileListView.SelectedItems)
            {
                disableItem(item);
            }
        }

        private static void disableItem(ListViewItem item)
        {
            item.Checked = false;
            item.ForeColor = Color.Gray;
        }

        public void removeFromBackupRestore(object data, EventArgs e)
        {
            removeSelectedItems();
        }

        public void toggleEnableDisableItem(object data, EventArgs e)
        {
            foreach (ListViewItem item in m_FileList.m_fileListView.Items)
            {
                if (item.Checked)
                {
                    disableItem(item);
                }
                else
                {
                    enableItem(item);
                }
            }
        }

        void m_fileListView_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                removeSelectedItems();
            }
        }

        private void removeSelectedItems()
        {
            ListView list = m_FileList.m_fileListView;
            ListView.SelectedIndexCollection indexes = list.SelectedIndices;
            int lastFocused = list.Items.IndexOf(list.FocusedItem);
            m_FileList.removeItems(indexes);
            // set the job as modified
            m_backupJob.m_Modified = true;
            if (lastFocused >= list.Items.Count)
            {
                lastFocused = list.Items.Count - 1;
            }
            if (lastFocused >= 0)
            {
                m_FileList.m_fileListView.EnsureVisible(lastFocused);
                m_FileList.m_fileListView.Items[lastFocused].Selected = true;
            }
        }

        /// <summary>
        /// Called when the job is initially created via the new button or loaded from disk
        /// </summary>
        /// <param name="job"></param>
        internal void setJob(BackupJob job)
        {
            if (m_backupJob != null && m_backupJob != job)
            {
                throw new BackupException("This tab is already associated with the job <" + job.GetKey() + ">");
            }
            m_backupJob = job;
            this.Text = job.m_Id.m_Name;
            m_FileList.setListOfItems(job.m_BackupPathList);
            m_FileList.setRestoreList(job.m_RestorePathList);
            string statusStr = "Last Run: " + getLastRunStatus(job);
            m_BackupDestPanel.updateStatus(statusStr,null,-1,true /*runStatusChanged*/);
        }


        /// <summary>
        /// Get the result of the last run attempt as a string
        /// </summary>
        /// <param name="job"></param>
        /// <returns></returns>
        private string getLastRunStatus(BackupJob job)
        {
            string statusStr = "";
            if (m_backupJob.m_lastRunSuccess == 0)
            {
                statusStr = "Backup job was never run.";
            }
            else
            {
                string s = "";
                switch (m_backupJob.m_lastRunSuccess)
                {
                    case BackupInfo.LastRunResult.FAILED:
                        {
                             s = " Failed ";
                        }
                        break;
                    case BackupInfo.LastRunResult.FINISHED:
                        {
                            s = " Success ";
                        }
                        break;
                    case BackupInfo.LastRunResult.ABORTED:
                        {
                            s = " Aborted by user";
                        }
                        break;
                }
                statusStr = "" //"Last run on "
                    + job.m_lastRunAttempted.ToString()
                    + " (" + s + ")"; 
            }
            return statusStr;
        }

        public string getScheduleString()
        {
            string str = m_backupJob.m_JobSchedule.getScheduleString();
            return str;
        }

        /// <summary>
        /// Status update handler is invoked for every operation during a backup/restore run.
        /// </summary>
        /// <param name="bInfo">Information about the backup job</param>
        public void statusUpdateHandler(BackupInfo bInfo)
        {
            BackupStatistics statistics = bInfo.m_Statistics;
            CurrentOperation operation = statistics.m_operation;
            BackupJobTab tab = (BackupJobTab)this;
            BackupJob job = tab.getBackupJob();
            string oprMsg = statistics.m_operationMsg;
            logStatusUpdate(bInfo);
            animateTabImage(operation);

            bool runStatusChanged = false;

            string info = "";
            switch (operation)
            {
                // Status update operation
                case CurrentOperation.Starting:
                case CurrentOperation.Resumed:
                    this.ImageIndex = ImageResources.Indexes.m_playButtonImage;
                    Text = m_backupJob.m_Id.m_Name + " ( Running ) ";
                    info = "Running\r\n";
                    runStatusChanged = true;
                    break;
                case CurrentOperation.Paused:
                    this.ImageIndex = ImageResources.Indexes.m_pauseButtonImage;
                    Text = m_backupJob.m_Id.m_Name + " ( Paused ) ";
                    info = "Paused\r\n";
                    runStatusChanged = true;
                    break;
                case CurrentOperation.EarlyExit:
                case CurrentOperation.Finished:
                    this.ImageIndex = ImageResources.Indexes.m_playButtonDisabledImage;
                    Text = m_backupJob.m_Id.m_Name;
                    info = "Not Running\r\n";
                    if (operation == CurrentOperation.EarlyExit)
                    {
                        runStatusChanged = false;
                    }
                    runStatusChanged = true;
                    break;
                case CurrentOperation.CopyFileStarting:
                case CurrentOperation.CopyFileEnd:
                    m_lastProgressReported = 0;
                    runStatusChanged = false;
                    break;
                default:
                    runStatusChanged = false;
                    break;
            };

            if (runStatusChanged)
            {
                updateToolTip(info);
            }
            tab.m_BackupDestPanel.updateStatus(oprMsg, bInfo, 1,runStatusChanged);
            //AppUI.showStatusMessage(oprMsgWithJobname);
        }

        /// <summary>
        /// Update the tooltip for the Job's Tab
        /// </summary>
        /// <param name="info"></param>
        public void updateToolTip(string runningStatus)
        {
            string toolTipText =
                string.Format("{0,-20}{1}\r\n{2,-20}{3}\r\n\r\n{4,-20}{5}\r\n{6,-20}{7}\r\n\r\n{8,-20}{9}\r\n{10,-20}{11}\r\n{12,-20}{13}",
                "Name:",
                m_backupJob.m_Id.m_Name,
                "Last Run:",
                getLastRunStatus(m_backupJob),
                "Guid:",
                m_backupJob.m_Id.m_Guid,
                "Destination:",
                m_backupJob.m_DestDir,
                "Schedule:",
                getScheduleString(),
                "Status:",
                runningStatus,
                "Created:",
                m_backupJob.m_createDate.ToLongDateString() + " " + m_backupJob.m_createDate.ToLongTimeString());

            this.ToolTipText = toolTipText;
        }

        private void animateTabImage(CurrentOperation operation)
        {

            long diff2 = DateTime.Now.Ticks - m_lastJobTabAnimated.Ticks;
            diff2 /= 10000L;
            int[] pausedAnimation = {
                ImageResources.Indexes.m_pauseButtonImage,
                ImageResources.Indexes.m_saveButtonImage,
                ImageResources.Indexes.m_saveMultipleButtonImage};

            int[] runAnimation = {
                ImageResources.Indexes.m_playButtonImage,
                ImageResources.Indexes.m_saveButtonImage,
                ImageResources.Indexes.m_saveMultipleButtonImage};

            if (diff2 > 450)
            {
                m_lastJobTabAnimated = DateTime.Now;
                m_animateImageIndex = (m_animateImageIndex + 1) % 3;
                this.ImageIndex = operation == CurrentOperation.InPause ? pausedAnimation[m_animateImageIndex] : runAnimation[m_animateImageIndex];
            }
        }

        private void logStatusUpdate(BackupInfo bInfo)
        {
            BackupStatistics statistics = bInfo.m_Statistics;
            CurrentOperation operation = statistics.m_operation;
            BackupJob job = bInfo.m_Job;
            string oprMsg = statistics.m_operationMsg;

            string oprMsgWithJobname = "Job <" + job.m_Id.m_Name + ">  " + oprMsg.Replace("\r\n", ",   ");

            if (operation == CurrentOperation.InPause)
            {
                // no need to report the pause state in the logs
            }
            else if (bInfo.m_LastException != null)
            {
                log.Warn(oprMsgWithJobname, bInfo.m_LastException);
            }
            else if (job.m_extraLoggingEnabled ||
                operation == CurrentOperation.Finished ||
                operation == CurrentOperation.Starting ||
                operation == CurrentOperation.Paused ||
                operation == CurrentOperation.Resumed ||
                operation == CurrentOperation.StatusUpdate)
            {
                log.Info(oprMsgWithJobname);
            }
            else if (operation == CurrentOperation.InvalidEntity ||
                operation == CurrentOperation.SystemError ||
                operation == CurrentOperation.AccessDenied
                )
            {
                log.Warn(oprMsgWithJobname);
            }
            else if (operation == CurrentOperation.CopyFileInProgress)
            {
                // log only when 10% progress has been made.
                long percent = bInfo.m_Statistics.m_fileStats.m_currentSize / 10;
                long diff = bInfo.m_Statistics.m_fileStats.m_inProgress - m_lastProgressReported;
                if (percent != 0 && (diff / percent > 0))
                {
                    m_lastProgressReported = bInfo.m_Statistics.m_fileStats.m_inProgress;
                    if (job.m_extraLoggingEnabled)
                    {
                        log.Info(oprMsgWithJobname);
                    }
                    else
                    {
                        log.Debug(oprMsgWithJobname);
                    }
                }
            }
            else
            {
                log.Debug(oprMsgWithJobname);
            }

        }


        public BackupJob getBackupJob() 
        {
            return m_backupJob;
        }

        /// <summary>
        /// The point at which the progress of the file copy operation was last reported.
        /// </summary>
        private long m_lastProgressReported = 0;
        private DateTime m_lastJobTabAnimated;

        /// <summary>
        /// The current index in use when animating the backup job tab while running/pausing.
        /// </summary>
        private int m_animateImageIndex = 0;
    };
}
